//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/GLshell/StateManager.h"

namespace Engine
{
namespace Graphics
{

	struct sampler_t
	{
		int	value;
		///
		sampler_t (int val = 0) : value(val) {}

		bool operator == (const sampler_t &other) const	{ return value == other.value; }
	};

	
	//
	// Cached Uniform Setter/Getter
	//
	
	template <typename T>
	struct CachedUniform
	{
		enum {
			typeId		= 0,
			arraySize	= 1,
		};
		typedef T	type;

		static void  _Warning ()		{ DESCTOP_ONLY( STATIC_WARNING( "not found realization of function for this type!" ) ); }

		static void Set (gles::GLuint location, T& oldValue, const T& newValue)			{ _Warning(); }
		static void Get (const gles::GLuint progId, gles::GLuint location, T& value)	{ _Warning(); }
	};


	template <typename T, usize Count>
	struct CachedUniform < static_array< T, Count > >
	{
		typedef CachedUniform< T >			_cu_type_t;
		typedef static_array< T, Count >	_value_t;

		enum {
			typeId		= _cu_type_t::typeId,
			arraySize	= Count,
		};

		typedef typename _cu_type_t::type	type;

		static void Set (gles::GLuint location, _value_t &oldValue, const _value_t &newValue)
		{
			struct Cmp
			{
				bool operator () (const T& left, const T& right) const
				{
					return All( left == right );
				}
			};

			if ( not uni_array< const T >( oldValue ).Compare( newValue, Cmp() ) ) {
				_cu_type_t::_Set( location, newValue.ptr(), newValue.Count() );
				oldValue = newValue;
			}
		}
		
		static void Get (const gles::GLuint progId, gles::GLuint location, _value_t& value)
		{
			_cu_type_t::_Get( progId, location, value.ptr() );
		}
	};


#	define DECL_UNIFORM_TYPE( _type, _typeId, _set, _get ) \
		template <> \
		struct CachedUniform <_type> \
		{ \
			enum { \
				typeId		= gles::_typeId, \
				arraySize	= 1, \
			}; \
			typedef _type		type; \
			\
			static void _Set (gles::GLuint location, const _type *value, uint count) \
			{ \
				using namespace gles; \
				\
				GL_CALL( _set ); \
			} \
			\
			static void Set (gles::GLuint location, _type& oldValue, const _type& newValue) \
			{ \
				if ( not All( oldValue == newValue ) ) { \
					_Set( location, &newValue, 1 ); \
					oldValue = newValue; \
				} \
			} \
			\
			static void Get (const gles::GLuint progId, gles::GLuint location, _type& value) \
			{ \
				return _Get( progId, location, &value ); \
			} \
			\
			static void _Get (const gles::GLuint progId, gles::GLuint location, _type *value) \
			{ \
				using namespace gles; \
				\
				GL_CALL( _get ); \
			} \
		}
	
	DECL_UNIFORM_TYPE(	sampler_t,
						GL_SAMPLER_2D,
						glUniform1i( location, value->value ),
						glGetUniformiv( progId, location, (GLint *) &value )
					 );
					 
	DECL_UNIFORM_TYPE(	int,
						GL_INT,
						glUniform1iv( location, count, value ),
						glGetUniformiv( progId, location, value )
					 );

	DECL_UNIFORM_TYPE(	ivec2,
						GL_INT_VEC2,
						glUniform2iv( location, count, (const GLint *) value ),
						glGetUniformiv( progId, location, (GLint *) value )
					 );

	DECL_UNIFORM_TYPE(	ivec3,
						GL_INT_VEC3,
						glUniform3iv( location, count, (const GLint *) value ),
						glGetUniformiv( progId, location, (GLint *) value )
					 );

	DECL_UNIFORM_TYPE(	ivec4,
						GL_INT_VEC4,
						glUniform4iv( location, count, (const GLint *) value ),
						glGetUniformiv( progId, location, (GLint *) value )
					 );

	DECL_UNIFORM_TYPE(	float,
						GL_FLOAT,
						glUniform1fv( location, count, value ),
						glGetUniformfv( progId, location, value )
					 );

	DECL_UNIFORM_TYPE(	vec2,
						GL_FLOAT_VEC2,
						glUniform2fv( location, count, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

	DECL_UNIFORM_TYPE(	vec3,
						GL_FLOAT_VEC3,
						glUniform3fv( location, count, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

	DECL_UNIFORM_TYPE(	vec4,
						GL_FLOAT_VEC4,
						glUniform4fv( location, count, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

	DECL_UNIFORM_TYPE(	mat2,
						GL_FLOAT_MAT2,
						glUniformMatrix2fv( location, count, GL_FALSE, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

	DECL_UNIFORM_TYPE(	mat3,
						GL_FLOAT_MAT3,
						glUniformMatrix3fv( location, count, GL_FALSE, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

	DECL_UNIFORM_TYPE(	mat4,
						GL_FLOAT_MAT4,
						glUniformMatrix4fv( location, count, GL_FALSE, (const GLfloat *) value ),
						glGetUniformfv( progId, location, (GLfloat *) value )
					 );

}	// Graphics
}	// Engine